package mth.rmclusterer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;

import com.rapidminer.Process;
import com.rapidminer.RapidMiner;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.tools.XMLException;

public class Manager {
	/*
	 * Der RMClusterer dient der massenhaften Auswertung der mit dem masterImp Programm
	 * erzeugten gewichteten Lösungen.
	 * Dazu führt die Manager-Klasse die initialen Clustervorgänge für jeden masterImp Durch-
	 * lauf aus. D.h., dass mit je 10 verschiedenen Seeds ein k-means Clustervorgang über den
	 * initialisierten RapidMiner durchgeführt wird. Die entstehenden Ergebnisse werden
	 * anschließend auf eine Konsenslösung zusammengefügt. Für diese werden die Kennzahlen
	 * F-Measure und violationRatio und violated Constraints berechnet. Als Referenz zur 
	 * Berechnung der F-Measure werden die Klassenzuweisungen der Originaldaten herangezogen.
	 * 
	 * Neben der einleitenden Clusterung steuert die Manager-Klasse den Prozess und stellt
	 * ähnlich der Tools Klasse in masterImp Werkzeuge als static Methoden bereit.
	 */
	public static String SOURCE_PATH_TO_VOTE="/home/freefreekk/Entwicklung/step5/method/dataset/files/";
	public static String DESTINATION_PATH_VOTE="/home/freefreekk/Entwicklung/step5/statistics/";
	public static String PATH_TO_NEIGHBORHOODS="/home/freefreekk/Entwicklung/step5/method/dataset/neighborhoods/";
	public static String VALIDATION_FILE="/home/freefreekk/Ubuntu One/masterImp/step1-standData/dataset";
	public static String PATH_TO_FILELIST="/home/freefreekk/Entwicklung/step4/method/filelist/dataset_all";
	public static String PATH_TO_FILES="/home/freefreekk/Entwicklung/step4/method/dataset/values_all/";
	public static String PATH_TO_RMPROCESS="/home/freefreekk/Entwicklung/step4/process/";
	public static String PATH_TO_WEIGHT_FILES="/home/freefreekk/Entwicklung/step4/method/dataset/weights/";
	
	public static int[] addIntegerToArray(int[] array, int val) {
		if (array==null || array.length==0) 
			array = new int[0];
		int[] helper = new int[array.length+1];
		for (int a=0; a<array.length; a++) {
			helper[a]=array[a];
		}
		helper[array.length] = val;
		array = new int[helper.length];
		array=helper;
		return array;
	}
	
	public static String[] addStringToArray(String[] array, String val) {
		if (array==null || array.length==0) 
			array = new String[0];
		String[] helper = new String[array.length+1];
		for (int a=0; a<array.length; a++) {
			helper[a]=array[a];
		}
		helper[array.length] = val;
		array = new String[helper.length];
		array=helper;
		return array;
	}
	
	private static String appendWeights(String qVal, String distMes) {
		File folder = new File(PATH_TO_WEIGHT_FILES);
		String[] files = folder.list();
		int marker=-1;
		for (int a=0; a<files.length && marker==-1; a++) {
			if (files[a].split("_")[2].equals(qVal) && files[a].split("_")[7].equals(distMes))
				marker=a;
		}
		String weightString="";
		BufferedReader lineRider;
		try {
			lineRider = new BufferedReader(new FileReader(PATH_TO_WEIGHT_FILES+files[marker]));
			String element=lineRider.readLine();
			while (element!=null) {
				weightString+=element;
				element = lineRider.readLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return weightString;
	}
	
	private static void createClusteredFiles(String dataset,String method) {
		try {
			try {
				RapidMiner.init();
				Process p = RapidMiner.readProcessFile(new File(PATH_TO_RMPROCESS+"clustering_with_mapping_"+method+"_"+dataset+".rmp"));
				String filelistpath=PATH_TO_FILELIST;
				String[] files = getInputFiles(filelistpath);
				
				//important: k has to be set manually, because the kValues of the files
				//are the beta-k-values!!!
				String k = getKValue(dataset);
				
				
				int seed=nextRandom();
				int[] lastSeeds = new int[1];
				lastSeeds[0]=seed;
				
				Operator readCSV = p.getOperator("Read CSV");
				Operator cluster = p.getOperator("Clustering");
				Operator writeCSV = p.getOperator("Write CSV");
				
				for (int seedNum=0; seedNum<10; seedNum++) {
					cluster.setParameter("local_random_seed", String.valueOf(seed));
					
					for (int fil=0; fil<files.length; fil++) {
						readCSV.setParameter("csv_file", PATH_TO_FILES+files[fil]);
						cluster.setParameter("numerical_measure", translateDistMeasure(getDistmeasureOfFile(files[fil])));
						cluster.setParameter("k",k);
						writeCSV.setParameter("csv_file", SOURCE_PATH_TO_VOTE+files[fil]+"_"+String.valueOf(seed));
							try {
								System.out.println(seedNum+":"+seed);
								p.run(0);
							} catch (OperatorException e) {
								e.printStackTrace();
							}
						}
					int tmp = nextRandom();
					while (intArrayContains(lastSeeds, tmp))
						tmp=nextRandom();
					seed=tmp;
					lastSeeds=addIntegerToArray(lastSeeds, seed);
				}
			} catch (InstantiationException e1) {
				e1.printStackTrace();
			} catch (IllegalAccessException e1) {
				e1.printStackTrace();
			}
		} catch (XMLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	public static String getDistmeasureOfFile(String fil) {
		String[] splitFil = fil.split("_");
		fil=translateDistMeasure(splitFil[7]);
		return fil;
	}
	
	public static String[] getInputFiles(String fileList) {
		String[] files = new String[0];
		BufferedReader lineRider;
		try {
			lineRider = new BufferedReader(new FileReader(fileList));
			String element=lineRider.readLine();
			while (element!=null) {
				files=addStringToArray(files, element);
				element = lineRider.readLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return files;
	}
	
	private static String getKValue(String s) {
		if (s.equals("iris")) {
			return "3";
		} else if (s.equals("yeast")) {
			return "10";
		} else if (s.equals("synth")) {
			return "3";
		} else if (s.equals("whitewine")) {
			return "6";
		}
		return "-1";
//		String[] parts = s.split("_");
//		return parts[4];
	}
	
	public static boolean intArrayContains(int[] array, int val) {
		if (array==null || array.length==0) 
			return false;
		for (int a=0; a<array.length; a++) {
			if (array[a]==val)
				return true;
		}
		return false;
	}
	
	public static int nextRandom() {
		Random r = new Random();
		int mod=1500;
		int ret=r.nextInt()%10;
		ret=(ret<0)?-ret:ret;
		do {
			for (int a=0; a<ret; a++) {
				ret=r.nextInt();
			}
		} while (ret%mod==453 || ret%mod==196 || ret%mod==35 || ret%mod==105 || 
				ret%mod==862 || ret%mod==6 || ret%mod==540 || ret%mod==371 || 
				ret%mod==400 || ret%mod==135 || ret%mod==840 || ret%mod==278 ||
				ret%mod==987 || ret%mod==137);
		return (ret>0)?ret%mod:-(ret%mod);
	}
	
	public static boolean stringArrayContains(String[] array, String val) {
		if (array==null || array.length==0) 
			return false;
		for (int a=0; a<array.length; a++) {
			if (array[a].equals(val))
				return true;
		}
		return false;
	}
	
	public static String translateDistMeasure(String distMeasure) {
		if (distMeasure.equals("Euclidian")) {
			distMeasure="EuclideanDistance";
		} else if (distMeasure.equals("Camberra")) {
			distMeasure="CamberraDistance";
		} else if (distMeasure.equals("Chebychev")) {
			distMeasure="ChebychevDistance";
		} else if (distMeasure.equals("CorrelationNumerical")) {
			distMeasure="CorrelationSimilarity";
		} else if (distMeasure.equals("DiceNumerical")) {
			distMeasure="DiceSimilarity";
		} else if (distMeasure.equals("DTWNumerical")) {
			distMeasure="DynamicTimeWarpingDistance";
		} else if (distMeasure.equals("InnerProduct")) {
			distMeasure="InnerProductSimilarity";
		} else if (distMeasure.equals("JaccardNumerical")) {
			distMeasure="JaccardSimilarity";
		} else if (distMeasure.equals("MaxProduct")) {
			distMeasure="MaxProductSimilarity";
		} else if (distMeasure.equals("OverlapNumerical")) {
			distMeasure="OverlapSimilarity";
		} else if (distMeasure.equals("CityBlok")) {
			distMeasure="ManhattanDistance";
		}
		return distMeasure;
	}
	
	public static void writeStatistics(String method, String dataset, String[] qValues, String[] distMeasures, String[] kValues, double[] hitRate, double[] fMeasure, 
			int[] violatedConstraints, double[] violationRatio, String[] pairCs, String[] realQVals, String[] nQVals) {
		String outString="distMeasure,k,qValues,realQValues,negativeQValues,constraints,violatedConstraints,violationRatio,hitRate,fMeasure,";
		if (dataset.equals("iris")) {
			outString+="w1,w2,w3,w4\n";
		} else if (dataset.equals("synth")) {
			outString+="w1,w2,w3,w4,w5\n";
		} else if (dataset.equals("whitewine")) {
			outString+="w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11\n";
		} else if (dataset.equals("yeast")) {
			outString+="w1,w2,w3,w4,w5,w6,w7,w8\n";
		}
		String file=method+"_"+dataset+"_statistics";
		for (int a=0; a<hitRate.length; a++) {
			outString+=distMeasures[a]+","+kValues[a]+","+qValues[a]+","+realQVals[a]+","+nQVals[a]+","+pairCs[a]+","+violatedConstraints[a]+","+violationRatio[a]+","+hitRate[a]+","+fMeasure[a]+",";
			outString+=appendWeights(qValues[a],distMeasures[a]);
			outString+="\n";
		}
		try {
			FileWriter fw = new  FileWriter(Manager.DESTINATION_PATH_VOTE+file, false);
			fw.write(outString);
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * @params: 
	 * -m to set the method --> Basu or me are allowed values
	 * -d to set the dataset
	 * both parameters are only for setting up the output folders
	 */
	public static void main(String[] args) {
		String method = "me"; //for debugging issues
		String dataset ="yeast";
		
		if (args!=null && args.length!=0) {
			for (int a=0; a<args.length; a++) {
				if (args[a].equals("-m")) {
					method = args[++a];
				} else if (args[a].equals("-d")) {
					dataset = args[++a];
				}
			}
		} else {
			System.out.println("Fehlende Parameter.");
			return;
		}
		if (!method.equals("") && !dataset.equals("")) {
			SOURCE_PATH_TO_VOTE = SOURCE_PATH_TO_VOTE.replaceAll("method", method);
			SOURCE_PATH_TO_VOTE = SOURCE_PATH_TO_VOTE.replaceAll("dataset", dataset);
			DESTINATION_PATH_VOTE = DESTINATION_PATH_VOTE.replaceAll("method", method);
			DESTINATION_PATH_VOTE = DESTINATION_PATH_VOTE.replaceAll("dataset", dataset);
			PATH_TO_NEIGHBORHOODS = PATH_TO_NEIGHBORHOODS.replaceAll("method", method);
			PATH_TO_NEIGHBORHOODS = PATH_TO_NEIGHBORHOODS.replaceAll("dataset", dataset);
			VALIDATION_FILE = VALIDATION_FILE.replaceAll("method", method);
			VALIDATION_FILE = VALIDATION_FILE.replaceAll("dataset", dataset);
			PATH_TO_FILELIST = PATH_TO_FILELIST.replaceAll("method", method);
			PATH_TO_FILELIST = PATH_TO_FILELIST.replaceAll("dataset", dataset);
			PATH_TO_FILES = PATH_TO_FILES.replaceAll("method", method);
			PATH_TO_FILES = PATH_TO_FILES.replaceAll("dataset", dataset);
			PATH_TO_WEIGHT_FILES = PATH_TO_WEIGHT_FILES.replaceAll("method", method);
			PATH_TO_WEIGHT_FILES = PATH_TO_WEIGHT_FILES.replaceAll("dataset", dataset);
		}
		
		Manager.createClusteredFiles(dataset,method);
		Evaluation ev = new Evaluation(getKValue(dataset));
		ev.consolidateRuns();
		
		ReferenceComparator rc = new ReferenceComparator(ev.getVotedRuns());
		rc.matchWithReference();
		
		BadNeighbors bn = new BadNeighbors(ev.getVotedRuns());
		bn.computeAmountOfBadNeighbors();
		
		Manager.writeStatistics(method, dataset,bn.getQValues(), bn.getDistanceMeasures(), bn.getKValues(), 
				rc.getHitRate(),rc.getFMeasure(), bn.getViolatedConstraints(), bn.getViolationRatio(),
				bn.getPairCounts(), bn.getRealQValues(), bn.getNQValues());
	}
}
